package yscmd

import (
	"context"
	"fmt"
	"gitee.com/kmyss/gf-ex/ysansi"
	"gitee.com/kmyss/gf-ex/ysbuild"
	"gitee.com/kmyss/gf-ex/yslog"
	"gitee.com/kmyss/gf-ex/ysutils"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gcmd"
	"github.com/gogf/gf/v2/os/gctx"
	"github.com/gogf/gf/v2/os/gproc"
	"github.com/gogf/gf/v2/text/gstr"
	"os"
	"os/signal"
	"time"
)

const (
	CmdShellUse = "shell"
	ctxKey      = "shell"
)

type CmdShellBuilder struct {
	CommandBuilderCore

	isShellRun bool
}

func (b *CmdShellBuilder) SetFlag(builder IFlagBuilder, _ ...string) bool {
	return false
}

func (b *CmdShellBuilder) Init(ctx context.Context) {
	b.CommandBuilderCore.Init(ctx)
	b.C.Run = b.run
	b.C.Use = CmdShellUse
	b.C.Short = "进入命令行控制模式"

}

func (b *CmdShellBuilder) SetChildCommand(builder ICommandBuilder) bool {
	return false
}

// run 命令行模式运行程序
func (b *CmdShellBuilder) run(c *Command) {
	if g.IsEmpty(c.parent.parent) {
		ysbuild.PrintLogo()
		ysbuild.PrintAppInfo()
	}

	gproc.AddSigHandlerShutdown(b.exit)
	ysutils.ProcInstanceListen()

	yslog.Printf(`进入 %s 命令的 shell 模式
- 使用 %s 查看更多帮助信息
- 返回上一级或退出请使用 %s`,
		c.parent.Use,
		ysansi.LightGreenStr("`help`"),
		ysansi.LightRedStr("`exit`"),
	)

	for {
		args := ScanAndLog(fmt.Sprintf("(%s)>> ", c.parent.Use))

		if args == "exit" {
			return
		} else if gstr.HasPrefix(args, "shell") {
			yslog.Print("已在 Shell 模式下，此命令不可用")
			continue
		} else if len(args) == 0 {
			time.Sleep(50 * time.Millisecond)
			continue
		} else if ysutils.IsDebugMode() {
			args += " --debug"
		}

		yslog.DebugV("父命令: ", c.parent.Use)
		yslog.DebugV("执行命令: ", args)

		// 将当前命令初始化进去
		gcmd.Init(append(g.SliceStr{c.parent.Use}, gstr.Split(args, " ")...)...)
		ctx := context.WithValue(gctx.New(), ctxKey, true)
		b.isShellRun = true
		c.parent.Exec(ctx)
		b.isShellRun = false
		gcmd.Init(os.Args...)
		ysutils.ProcInstanceListen()
	}

}

func (b *CmdShellBuilder) exit(sig os.Signal) {
	if !b.isShellRun {
		yslog.Print("\n请再次点击 Ctrl + C")
	}
	signal.Reset(sig)
}

func IsShell(ctx context.Context) bool {
	return ctx.Value(ctxKey) != nil
}
